package cn.jtfadmin.core.modules.sys.aspect;//package cn.jtfadmin.core.modules.sys.aspect;

import cn.jtfadmin.base.lang.common.domain.dto.SpecConditionDTO;
import cn.jtfadmin.base.lang.common.event.EntityEvent;
import cn.jtfadmin.base.lang.common.service.BaseTreeService;
import cn.jtfadmin.base.lang.common.utils.ExecuterUtils;
import cn.jtfadmin.base.lang.common.utils.RequestUtils;
import cn.jtfadmin.base.lang.enums.EntityEventEnum;
import cn.jtfadmin.core.enums.MenuTypeEnum;
import cn.jtfadmin.core.enums.OperateTypeEnum;
import cn.jtfadmin.core.modules.sys.domain.entity.SysPermissionEntity;
import cn.jtfadmin.core.modules.sys.service.EntityLastDateTimeService;
import cn.jtfadmin.core.modules.sys.service.SysLogActionService;
import lombok.Data;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;

import javax.cache.CacheManager;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 日志范围aop
 * 作用记录日志<br/>
 * @see SysLogActionService#newOperateLog(String, Long, OperateTypeEnum, String, Exception)
 */
@Aspect
@Component
public class LogAspect  {


    private Logger logger = LoggerFactory.getLogger(LogAspect.class);


    @Autowired
    private SysLogActionService sysLogActionService;


    @Autowired
    private BaseTreeService<SysPermissionEntity> sysPermissionEntityBaseTreeService;

    @Autowired
    private EntityLastDateTimeService entityLastDateTimeService;


    private PathMatcher pathMatcher = new AntPathMatcher();

    private List<SysPermissionDTO> auths;

    private LocalDateTime updateDateTime;

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)||" +
            "@annotation(org.springframework.web.bind.annotation.GetMapping)||" +
            "@annotation(org.springframework.web.bind.annotation.PostMapping)||" +
            "@annotation(org.springframework.web.bind.annotation.DeleteMapping)||" +
            "@annotation(org.springframework.web.bind.annotation.PutMapping)")
    public void pointCut() {
    }




    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request = RequestUtils.getRequest();
        // 非web 请求，不拦截
        if (Objects.isNull(request)) {
            return point.proceed();
        }
        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();
        String path = uri.substring(contextPath.length());
        Optional<SysPermissionDTO> optional = getMatchPermission(path);
        // 无对应规则， 不拦截
        if (!optional.isPresent()) {
            return point.proceed();
        }

        long currentStamp = System.currentTimeMillis();
        Exception e = null;
        Object proceedResult = null;
        Object[] args = point.getArgs();
        try {
            proceedResult = point.proceed();
        } catch (Exception ex) {
            e = ex;
        }
        long currentStamp2 = System.currentTimeMillis();
        SysPermissionDTO info = optional.get();
        OperateTypeEnum operateType = info.getOperateType();
        if (Objects.nonNull(operateType) && !OperateTypeEnum.SELECT.equals(operateType)) {
            sysLogActionService.newOperateLog(
                    info.getName(), currentStamp2 - currentStamp,
                    operateType, Arrays.toString(args), e
            );
        }
        if (Objects.nonNull(e)) {
            throw e;
        }
        return proceedResult;
    }


    private SysPermissionEntity findParent(SysPermissionEntity t, Collection<SysPermissionEntity> all){
        for (SysPermissionEntity sysPermissionEntity : all) {
            if(sysPermissionEntity.getId().equals(t.getParentId())){
                return sysPermissionEntity;
            }
        }
        return null;
    }

    private String getPermissonAllName(SysPermissionEntity t, Collection<SysPermissionEntity> all){
        List<String> names = new ArrayList<>(4);
        names.add(t.getName());
        SysPermissionEntity p = findParent(t, all);
        while (p!=null){
            names.add(p.getName());
            p = findParent(p, all);
        }
        Collections.reverse(names);
        return StringUtils.collectionToDelimitedString(names, "-");
    }

    private Optional<SysPermissionDTO> getMatchPermission(String path) {
        boolean updateFlag = false;
        if(Objects.isNull(updateDateTime)){
            updateFlag = true;
        }else {
            Optional<LocalDateTime> localDateTimeOptional = entityLastDateTimeService.lastDateTime(SysPermissionEntity.class);
            if(localDateTimeOptional.isPresent() && localDateTimeOptional.get().isAfter(updateDateTime)){
                updateFlag = true;
            }
        }
        if(updateFlag) {
            updateDateTime = LocalDateTime.now();
            List<SysPermissionEntity> allPermission = ExecuterUtils.exec(() -> sysPermissionEntityBaseTreeService.findAll((List<SpecConditionDTO>) null));
            List<SysPermissionEntity> permissionEntities = allPermission.stream().filter(t -> MenuTypeEnum.PERMISSION.equals(t.getMenuType())).collect(Collectors.toList());
            List<SysPermissionDTO> list = new ArrayList<>(permissionEntities.size());
            for (SysPermissionEntity permissionEntity : permissionEntities) {
                if(StringUtils.hasText(permissionEntity.getUrl())){
                    Set<String> urls = StringUtils.commaDelimitedListToSet(permissionEntity.getUrl());
                    for (String url : urls) {
                        SysPermissionDTO dto = new SysPermissionDTO();
                        dto.setName(getPermissonAllName(permissionEntity, allPermission));
                        dto.setUrl(url);
                        dto.setOperateType(permissionEntity.getOperateType());
                        list.add(dto);
                    }
                }
            }
            auths = list;
        }
            List<SysPermissionDTO> matchs =
                auths.stream()
                        .filter(t->pathMatcher.match(t.getUrl(), path))
                        .sorted((t1, t2)->t2.getUrl().length()-t1.getUrl().length())
                .collect(Collectors.toList());
        if(matchs.isEmpty()){
            return Optional.empty();
        }
        return Optional.of(matchs.get(0));
    }


    @Data
    public static class SysPermissionDTO{
        protected String name;


        protected String url;

        protected OperateTypeEnum operateType;


    }
}
